<?php

namespace App\Exports;

use Illuminate\Support\Facades\Auth;
use League\Csv\Writer;

abstract class Export
{
    use ExportTrait;

    public function __construct()
    {
        // 设置超时时间，300秒
        set_time_limit(300);
    }

    /**
     * 文件名称
     *
     * @var string
     */
    protected $filename;

    /**
     * 分页条数
     *
     * @var int
     */
    protected $size = 5000;

    /**
     * 最大页数
     *
     * @var int
     */
    protected $pageMax = 200;

    /**
     * 页数
     *
     * @var int
     */
    protected $page = 1;

    /**
     * scroll单次超时时间
     *
     * @var string
     */
    protected $scroll = '1m';

    /**
     * 请求参数
     *
     * @var \Illuminate\Http\Request
     */
    protected $request;

    /**
     * 设置请求参数
     *
     * @param \Illuminate\Http\Request $request
     * @return $this
     */
    public function setRequest($request)
    {
        $this->request = $request;

        return $this;
    }

    /**
     * 平台列表
     *
     * @var array
     */
    protected $orgs;

    /**
     * 初始化平台列表，code为下标
     */
    protected function initOrgs()
    {
        $this->orgs = Auth::user()->getPermissionOrgs()->keyBy('code')->toArray();
    }

    /**
     * CSV 文件对象
     *
     * @var Writer
     */
    protected $csv;

    /**
     * 获取 CSV 文件对象
     *
     * @return Writer
     */
    protected function csv(): Writer
    {
        if (is_null($this->csv)) {
            // 创建文件对象
            $this->csv = Writer::createFromFileObject(new \SplTempFileObject());
            // 设置字符编码
            $this->csv->setOutputBOM(Writer::BOM_UTF8);
        }
        return $this->csv;
    }

    /**
     * Database Builder
     *
     * @var \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder
     */
    protected $query;

    /**
     * Set Database Builder
     *
     * @param \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder $query
     * @return static
     */
    protected function query($query)
    {
        $this->query = $query;

        return $this;
    }

    /**
     * Collection
     *
     * @var \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Collection
     */
    protected $collection;

    /**
     * Set Collection
     *
     * @param \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Collection $collection
     * @return static
     */
    protected function collection($collection)
    {
        $this->collection = $collection;

        return $this;
    }

    /**
     * Arrays
     *
     * @var array[]
     */
    protected $arrays;

    /**
     * Set Arrays
     *
     * @param $arrays
     * @return $this
     */
    protected function array($arrays)
    {
        $this->arrays = $arrays;

        return $this;
    }

    /**
     * Elasticsearch Results
     *
     * @var array
     */
    protected $results;

    /**
     * Elasticsearch Client
     *
     * @var \Elasticsearch\Client
     */
    protected $client;

    /**
     * Set Elasticsearch
     *
     * @param array $params
     * @param \App\Models\Elasticsearch\Model $model
     * @return $this
     */
    protected function es($params, $model)
    {
        // 分页参数
        $params += ['size' => $this->size, 'scroll' => $this->scroll];
        // 查询数据
        $this->results = $model::search($params);

        // Client
        $this->client = $model::client();

        return $this;
    }

    /**
     * 导出 CSV 文件
     *
     * @param string|null $filename
     * @return void
     */
    public function download(string $filename = null)
    {
        // 初始化操作
        $this->init();

        // 添加标题行
        $this->csv()->insertOne($this->headings());

        // 添加数据行
        $callback = function ($item) {
            $this->csv->insertOne($this->map($item));
        };

        // 数据分类处理
        if (!is_null($this->results)) {
            // Elasticsearch Scroll
            while (count($this->results['hits']['hits'])) {
                // 处理上一批数据
                collect($this->results['hits']['hits'])->each($callback);
                // 获取下一批数据
                $this->results = $this->client->scroll([
                    'scroll_id' => $this->results['_scroll_id'],
                    'scroll' => $this->scroll,
                ]);
                // 最大导出数量
                if ($this->page++ >= $this->pageMax) {
                    break;
                }
            }
            // 清除滚动
            $this->client->clearScroll([
                'scroll_id' => $this->results['_scroll_id'],
            ]);
        } elseif (!is_null($this->query)) {
            // Database Builder
            $this->query->chunk($this->size, function ($collection) use ($callback) {
                // 处理一批数据
                $collection->each($callback);
                // 最大导出数量
                if ($this->page++ >= $this->pageMax) {
                    return false;
                }
            });
        } elseif (!is_null($this->collection)) {
            // 集合
            $this->collection->each($callback);
        } elseif (!is_null($this->arrays)) {
            // 数组
            collect($this->arrays)->each($callback);
        }

        // 输出CSV文件
        $this->csv->output(($filename ?: $this->filename) . date('.YmdHis') . '.csv');
        die();
    }

    /**
     * 初始化操作【可覆盖】
     */
    protected function init()
    {
        // 一些自定义操作
    }

    /**
     * 映射标题行
     *
     * @return array
     */
    abstract protected function headings(): array;

    /**
     * 映射数据行
     *
     * @param \Illuminate\Database\Eloquent\Model|array $item
     * @return array
     */
    abstract protected function map($item): array;
}
